package com.bluesky.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bluesky.common.Constants;
import com.bluesky.common.enums.LinkExternalEnum;
import com.bluesky.common.enums.MenuTypeEnum;
import com.bluesky.common.enums.StatusEnum;
import com.bluesky.common.exception.CustomException;
import com.bluesky.common.util.SecurityUtils;
import com.bluesky.system.common.dto.SysMenuAddDTO;
import com.bluesky.system.common.dto.SysMenuEditDTO;
import com.bluesky.system.common.dto.SysMenuQueryDTO;
import com.bluesky.system.entity.SysMenu;
import com.bluesky.system.entity.SysRoleMenu;
import com.bluesky.system.entity.SysUserRole;
import com.bluesky.system.mapper.SysMenuMapper;
import com.bluesky.system.service.ISysMenuService;
import com.bluesky.system.service.ISysRoleMenuService;
import com.bluesky.system.service.ISysUserRoleService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表  服务实现类
 * </p>
 *
 * @author Kevin
 * @since 2021-06-19
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {

    @Resource
    private ISysUserRoleService sysUserRoleService;

    @Resource
    private ISysRoleMenuService sysRoleMenuService;

    @Override
    public IPage<SysMenu> page(Page reqPage, SysMenuQueryDTO req) {
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Objects.nonNull(req.getParentId()), SysMenu::getParentId, req.getParentId());
        queryWrapper.like(StrUtil.isNotBlank(req.getMenuName()), SysMenu::getMenuName, req.getMenuName());
        queryWrapper.orderByAsc(SysMenu::getSort);
        IPage<SysMenu> page = this.page(reqPage, queryWrapper);
        List<SysMenu> sysMenuList = this.list();
        page.getRecords().forEach(item -> {
            item.setChildren(getChildrenList(sysMenuList, item));
            if (item.getParentId().intValue() == 0) {
                item.setParentId(null);
            }
        });
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(SysMenuAddDTO req) {
        SysMenu entity = BeanUtil.copyProperties(req, SysMenu.class);
        if (Objects.isNull(req.getParentId())) {
            entity.setParentId(0L);
        }
        this.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(SysMenuEditDTO req) {
        SysMenu entity = BeanUtil.copyProperties(req, SysMenu.class);
        if (Objects.isNull(req.getParentId())) {
            entity.setParentId(0L);
        }
        if (entity.getMenuType().equals(MenuTypeEnum.DIR.getCode())) {
            entity.setComponent("");
            entity.setPermission("");
            entity.setKeepalive("");
            entity.setLinkExternal("");
            entity.setFrame("");
            entity.setLinkUrl("");
        }
        if (entity.getMenuType().equals(MenuTypeEnum.MENU.getCode())) {
            entity.setPermission("");
            if (entity.getLinkExternal().equals(LinkExternalEnum.NO.getCode())) {
                entity.setFrame("");
                entity.setLinkUrl("");
            }
        }
        if (entity.getMenuType().equals(MenuTypeEnum.BUTTON.getCode())) {
            entity.setIcon("");
            entity.setRoutePath("");
            entity.setComponent("");
            entity.setVisible("");
            entity.setKeepalive("");
            entity.setLinkExternal("");
            entity.setFrame("");
            entity.setLinkUrl("");
        }
        this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        idList.forEach(item -> {
            SysMenu sysMenu = this.getById(item);
            SysMenu sysMenuChild = this.getOne(Wrappers.lambdaQuery(SysMenu.class).eq(SysMenu::getParentId, item).last("LIMIT 1"));
            if (Objects.nonNull(sysMenuChild)) {
                throw new CustomException(String.format("菜单【%s】存在下级菜单，无法删除", sysMenu.getMenuName()));
            }
        });
        this.removeByIds(Arrays.asList(ids.split(",")));
    }

    @Override
    public SysMenu view(String id) {
        return this.getById(id);
    }

    @Override
    public List<Tree<String>> listMenuTree() {
        List<SysMenu> sysMenuList = this.list();
        if (sysMenuList.isEmpty()) {
            return Lists.newArrayList();
        }
        List treeNodeList = sysMenuList.stream().map(item -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setId(item.getId().toString());
            treeNode.setName(item.getMenuName());
            treeNode.setParentId(item.getParentId().toString());
            treeNode.setWeight(item.getSort());
            Map extra = Maps.newHashMap();
            extra.put("icon", item.getIcon());
            treeNode.setExtra(extra);
            return treeNode;
        }).collect(Collectors.toList());
        List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
        return treeList;
    }

    @Override
    public List<Tree<String>> selectMenuTree() {
        List<SysMenu> sysMenuList;
        if (SecurityUtils.getTenantCode().equals(Constants.ADMIN_TENANT)) {
            sysMenuList = this.list();
        } else {
            sysMenuList = this.listSysMenuByUserId(SecurityUtils.getSysUser().getId());
        }
        if (sysMenuList.isEmpty()) {
            return Lists.newArrayList();
        }
        List treeNodeList = sysMenuList.stream().map(item -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setId(item.getId().toString());
            treeNode.setName(item.getMenuName());
            treeNode.setParentId(item.getParentId().toString());
            treeNode.setWeight(item.getSort());
            Map extra = Maps.newHashMap();
            extra.put("icon", item.getIcon());
            treeNode.setExtra(extra);
            return treeNode;
        }).collect(Collectors.toList());
        List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
        return treeList;
    }

    @Override
    public Set<String> getPermissionsByUserId(Long userId) {
        // 查询角色列表
        List<SysUserRole> sysUserRoleList = sysUserRoleService.list(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
        if (sysUserRoleList.isEmpty()) {
            return Sets.newHashSet();
        }
        List<Long> roleIdList = sysUserRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
        // 查询按钮菜单权限标识
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(Wrappers.lambdaQuery(SysRoleMenu.class).in(SysRoleMenu::getRoleId, roleIdList));
        if (sysRoleMenuList.isEmpty()) {
            return Sets.newHashSet();
        }
        // 查询菜单
        Set<Long> menuIds = sysRoleMenuList.stream().map(item -> item.getMenuId()).collect(Collectors.toSet());
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(SysMenu::getMenuType, MenuTypeEnum.BUTTON.getCode());
        queryWrapper.eq(SysMenu::getStatus, StatusEnum.YES.getCode());
        queryWrapper.in(SysMenu::getId, menuIds);
        List<SysMenu> sysMenuList = this.list(queryWrapper);
        return sysMenuList.stream().map(item -> item.getPermission()).collect(Collectors.toSet());
    }

    @Override
    public List<SysMenu> listGrantMenuByUserId(Long userId) {
        // 查询角色列表
        List<SysUserRole> sysUserRoleList = sysUserRoleService.list(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
        if (sysUserRoleList.isEmpty()) {
            return Lists.newArrayList();
        }
        List<Long> roleIdList = sysUserRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
        // 查询角色菜单
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(Wrappers.lambdaQuery(SysRoleMenu.class).in(SysRoleMenu::getRoleId, roleIdList));
        if (sysRoleMenuList.isEmpty()) {
            return Lists.newArrayList();
        }
        // 查询菜单
        Set<Long> menuIds = sysRoleMenuList.stream().map(item -> item.getMenuId()).collect(Collectors.toSet());
        LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(SysMenu::getStatus, StatusEnum.YES.getCode());
        queryWrapper.in(SysMenu::getMenuType, MenuTypeEnum.DIR.getCode(), MenuTypeEnum.MENU.getCode());
        queryWrapper.in(SysMenu::getId, menuIds);
        return this.list(queryWrapper);
    }

    private List<SysMenu> listSysMenuByUserId(Long userId) {
        // 查询角色列表
        List<SysUserRole> sysUserRoleList = sysUserRoleService.list(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
        if (sysUserRoleList.isEmpty()) {
            return Lists.newArrayList();
        }
        List<Long> roleIdList = sysUserRoleList.stream().map(item -> item.getRoleId()).collect(Collectors.toList());
        // 查询角色菜单
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(Wrappers.lambdaQuery(SysRoleMenu.class).in(SysRoleMenu::getRoleId, roleIdList));
        if (sysRoleMenuList.isEmpty()) {
            return Lists.newArrayList();
        }
        // 查询菜单
        Set<Long> menuIds = sysRoleMenuList.stream().map(item -> item.getMenuId()).collect(Collectors.toSet());
        return this.list(Wrappers.lambdaQuery(SysMenu.class).in(SysMenu::getId, menuIds));
    }

    private List<SysMenu> getChildrenList(List<SysMenu> sysMenuList, SysMenu root) {
        List<SysMenu> childrenList = sysMenuList.stream()
                .filter(item -> item.getParentId().equals(root.getId()))
                .sorted(Comparator.comparing(SysMenu::getSort))
                .collect(Collectors.toList());
        childrenList.forEach(item -> {
            item.setChildren(getChildrenList(sysMenuList, item));
        });
        return childrenList;
    }

}
